﻿using Newtonsoft.Json;
using System;

namespace StackExchange.Redis.Extensions.MQ
{
    public static class IDatabaseExtension
    {
        private const string MQ_NAME = "DefaultMQ";

        private const string SEC_MQ_NAME = "SECOND_MQ";

        /// <summary>
        /// Pop message from default message queue
        /// </summary>
        /// <typeparam name="TResult">The custom object that reture from func</typeparam>
        /// <param name="redisDb">redis database</param>
        /// <param name="func">business logic</param>
        /// <returns></returns>
        public static TResult PopMessage<TResult>(this IDatabase redisDb, string deploymentId, Func<string, TResult> func)
        {
            TResult result = default(TResult);

            var value = Pop(redisDb, MQ_NAME, SEC_MQ_NAME);

            if (!string.IsNullOrEmpty(value))
            {
                var msg = JsonConvert.DeserializeObject<Msg>(value);

                if(msg != null)
                {
                    using (var heartbeat = new MessageQueueHeartbeat(redisDb, SEC_MQ_NAME, value))
                    {
                        try
                        {
                            result = func(msg.Value);
                        }
                        catch
                        {
                            redisDb.PushMessage(MQ_NAME, deploymentId, msg.Value);
                        }
                    }

                    redisDb.ListRemove(SEC_MQ_NAME, value, 1);
                }
                
            }

            return result;

        }

        /// <summary>
        /// push message to default MQ
        /// </summary>
        /// <param name="redisDb"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public static long PushMessage(this IDatabase redisDb, string value)
        {
            var message = new Msg { MessageId = Guid.NewGuid().ToString(), Value = value };

            return redisDb.ListLeftPush(MQ_NAME, JsonConvert.SerializeObject(message), When.Always);
        }

        /// <summary>
        /// Push the message to message queue
        /// </summary>
        /// <param name="redisDb">redis database</param>
        /// <param name="mqName">message queue name</param>
        /// <param name="value">message it will be pushed to MQ</param>
        /// <returns>the message quque length after the push operations</returns>
        public static long PushMessage(this IDatabase redisDb, string deploymentId, string mqName, string value)
        {
            var message = new Msg { MessageId = Guid.NewGuid().ToString(), Value = value, DevelopmentId = deploymentId };

            return redisDb.ListLeftPush(mqName, JsonConvert.SerializeObject(message), When.Always);
        }

        /// <summary>
        /// Pop message from specify message queue
        /// </summary>
        /// <param name="redisDb">redis database</param>
        /// <param name="mqName">MQ name</param>
        /// <returns>pop message</returns>
        public static string PopMessage(this IDatabase redisDb, string mqName)
        {
            var message = redisDb.ListRightPop(mqName);

            var msg = JsonConvert.DeserializeObject<Msg>(message);

            if (msg != null)
                return msg.Value;
            else
                return string.Empty;

        }

        public static void ResumeMessage(this IDatabase redisDb, string demploymentId, string resumedMqName = MQ_NAME, string backMqName = SEC_MQ_NAME)
        {
            var list = redisDb.ListRange(SEC_MQ_NAME);
            foreach(var l in list )
            {
                var message = JsonConvert.DeserializeObject<Msg>(l);
                if(message.DevelopmentId != demploymentId)
                {
                    redisDb.ListRemove(backMqName, l);
                    redisDb.PushMessage(demploymentId, resumedMqName, message.Value);
                }
            }
        }

        private static string Pop(IDatabase redisDb, string mqName, string backMqName)
        {
            return redisDb.ListRightPopLeftPush(mqName, backMqName);
        }
    }
}
